package cn.ges.ali

import com.alipay.api.AlipayApiException
import com.alipay.api.CertAlipayRequest
import com.alipay.api.DefaultAlipayClient
import com.alipay.api.internal.util.AlipaySignature
import com.alipay.api.request.AlipayFundAccountQueryRequest
import com.alipay.api.request.AlipayFundTransUniTransferRequest
import com.alipay.api.request.AlipaySystemOauthTokenRequest
import com.alipay.api.request.AlipayUserUserinfoShareRequest
import com.alipay.api.response.AlipayFundAccountQueryResponse
import com.alipay.api.response.AlipayFundTransUniTransferResponse
import com.alipay.api.response.AlipaySystemOauthTokenResponse
import com.alipay.api.response.AlipayUserUserinfoShareResponse
import java.io.File
import java.math.BigDecimal
import java.math.RoundingMode
import java.net.URLEncoder


/**
 *
 * @author gespent@163.com
 * @date 2020/8/28 10:51
 */
open class AliPayHelper {

    companion object {
        lateinit var alipayAppId: String
        lateinit var alipaySellerId: String
        lateinit var alipayAppPriKey: String
        var useCertAliPayClient: Boolean = false


        lateinit var aliPayClient: DefaultAlipayClient

        fun initAliPayClient(serviceUrl: String?, alipayAppId: String, alipayAppPriKey: String,
                             alipayPublicKey: String, alipaySellerId: String) {
            AliPayHelper.alipayAppId = alipayAppId
            AliPayHelper.alipaySellerId = alipaySellerId
            AliPayHelper.alipayAppPriKey = alipayAppPriKey

            val url = serviceUrl ?: "https://openapi.alipay.com/gateway.do"
            aliPayClient = DefaultAlipayClient(url, alipayAppId, alipayAppPriKey,
                    "json", "utf-8", alipayPublicKey, "RSA2");
        }

        fun initAliPayClientWithCert(serviceUrl: String?, alipayAppId: String, alipayAppPriKey: String, alipaySellerId: String,
                                     appCertPath: String, alipayCertPath: String, alipayRootCertPath: String) {

            AliPayHelper.alipayAppId = alipayAppId
            AliPayHelper.alipaySellerId = alipaySellerId
            AliPayHelper.alipayAppPriKey = alipayAppPriKey

            if (!File(appCertPath).exists()) {
                throw Exception("初始化支付宝客户端失败 appCertPath上不存在文件")
            }
            if (!File(alipayCertPath).exists()) {
                throw Exception("初始化支付宝客户端失败 alipayCertPath上不存在文件")
            }
            if (!File(alipayRootCertPath).exists()) {
                throw Exception("初始化支付宝客户端失败 alipayRootCertPath上不存在文件")
            }
            val certAlipayRequest = CertAlipayRequest()
            certAlipayRequest.serverUrl = serviceUrl ?: "https://openapi.alipay.com/gateway.do"
            certAlipayRequest.appId = alipayAppId
            certAlipayRequest.privateKey = alipayAppPriKey
            certAlipayRequest.format = "json"
            certAlipayRequest.charset = "UTF-8"
            certAlipayRequest.signType = "RSA2"
            certAlipayRequest.certPath = appCertPath
            certAlipayRequest.alipayPublicCertPath = alipayCertPath
            certAlipayRequest.rootCertPath = alipayRootCertPath

            useCertAliPayClient = true
            aliPayClient = DefaultAlipayClient(certAlipayRequest)
        }

        /**
         * @param amount 实际打款数值(单位元) 会强制转换为2位小数,算法为DOWN
         * 执行支付宝提现
         * 此处不校验支付宝余额
         */
        fun executeWithdrawalWithCert(orderId: Long, amount: BigDecimal, title: String, aliPayUserId: String): Pair<Boolean, String?> {
            val request = AlipayFundTransUniTransferRequest()
            val scale = amount.stripTrailingZeros().setScale(2, RoundingMode.DOWN)
            if (BigDecimal(0.01) > scale) {
                return Pair(false, "提现金额过小")
            }
            request.bizContent = "{" +
                    // 商户端的唯一订单号，对于同一笔转账请求，商户需保证该订单号唯一
                    "\"out_biz_no\":\"${orderId}\"," +
                    // 订单总金额，单位为元，精确到小数点后两位，取值范围[0.01,100000000]
                    "\"trans_amount\":${scale.toPlainString()}," +
                    // 销售产品码，单笔无密转账固定为TRANS_ACCOUNT_NO_PWD
                    "\"product_code\":\"TRANS_ACCOUNT_NO_PWD\"," +
                    // 业务场景，单笔无密转账固定为DIRECT_TRANSFER
                    "\"biz_scene\":\"DIRECT_TRANSFER\"," +
                    // 转账业务的标题，用于在支付宝用户的账单里显示。
                    "\"order_title\":\"${title}\"," +
                    // Participant类型，收款方信息
                    "\"payee_info\":{" +
                    // 参与方的标识ID，比如支付宝用户UID。
                    "\"identity\":\"${aliPayUserId}\"," +
                    /*
                        参与方的标识类型，目前支持如下枚举：
                        1、ALIPAY_USER_ID 支付宝的会员ID
                        2、ALIPAY_LOGON_ID：支付宝登录号，支持邮箱和手机号格式
                     */
                    "\"identity_type\":\"ALIPAY_USER_ID\"" +
                    "  }}"
            // 执行请求
            val response: AlipayFundTransUniTransferResponse = aliPayClient.certificateExecute(request)
            return if (response.isSuccess) {
                // 返回流水号
                Pair(true, response.payFundOrderId)
            } else {
                Pair(false, response.status)
            }
        }

        /**
         * 获得支付宝账户余额
         */
        fun getAccountBalance(): Int {
            val request = AlipayFundAccountQueryRequest()
            request.bizContent = "{" +
                    "\"alipay_user_id\":\"${alipaySellerId}\"," +
                    "\"account_type\":\"ACCTRANS_ACCOUNT\"" +
                    "  }";
            val response: AlipayFundAccountQueryResponse = if (useCertAliPayClient) {
                aliPayClient.certificateExecute(request)
            } else {
                aliPayClient.execute(request)
            }
            return if (response.isSuccess) {
                BigDecimal(response.availableAmount ?: "0").multiply(BigDecimal(1000)).intValueExact()
            } else {
                0
            }
        }


        fun getUserInfoByAuthCode(authCode: String): java.util.HashMap<String, Any?> {
            val map = java.util.HashMap<String, Any?>(3)
            // 获得accessToken
            val acRequest = AlipaySystemOauthTokenRequest()
            acRequest.grantType = "authorization_code"
            acRequest.code = authCode
            val acResponse: AlipaySystemOauthTokenResponse
            try {
                acResponse = if (useCertAliPayClient) {
                    aliPayClient.certificateExecute(acRequest)
                } else {
                    aliPayClient.execute(acRequest)
                }
            } catch (e: Exception) {
                e.printStackTrace()
                map["state"] = false
                return map
            }

            if (acResponse != null && acResponse.isSuccess) {
                val accessToken = acResponse.accessToken
                if (accessToken != null) {
                    // 获取用户数据 https://opendocs.alipay.com/apis/api_33/alipay.user.userinfo.share
                    val atRequest = AlipayUserUserinfoShareRequest()
                    val atResponse: AlipayUserUserinfoShareResponse
                    try {
                        atResponse = if (useCertAliPayClient) {
                            aliPayClient.certificateExecute(atRequest, accessToken)
                        } else {
                            aliPayClient.execute(atRequest, accessToken)
                        }
                    } catch (e: AlipayApiException) {
                        e.printStackTrace()
                        map["return"] = -1
                        return map
                    }
                    if (atResponse != null && atResponse.isSuccess) {
                        /*
                     * 用户状态（Q/T/B/W）。
                     * Q代表快速注册用户
                     * T代表已认证用户
                     * B代表被冻结账户
                     * W代表已注册，未激活的账户
                     */
                        when (atResponse.userStatus) {
                            "B" -> {
                                // 该用户已经被支付宝冻结
                                map["state"] = false
                                return map
                            }
                            "W" -> {
                                // 该用户已注册 但是未激活
                                map["state"] = false
                                return map
                            }
                            else -> {
                            }
                        }
                        map["state"] = true
                        // 	支付宝用户ID
                        map["aliPayUserId"] = atResponse.alipayUserId
                        map["certified"] = atResponse.isCertified ?: false
                        return map
                    }
                }
            }
            // token无效 或者支付宝拒绝服务
            map["state"] = false
            return map
        }

        fun getAuthUrl4App(userId: Long): String? {
            val map = HashMap<String, String>(10)
            //  服务对应的名称
            //  服务对应的名称
            map["apiname"] = "com.alipay.account.auth"
            //  接口名称，常量值为alipay.open.auth.sdk.code.get
            map["method"] = "alipay.open.auth.sdk.code.get"
            // 支付宝分配给开发者的应用ID
            map["app_id"] = alipayAppId
            // 	 调用来源方的标识，常量值为mc
            map["app_name"] = "mc"
            //   调用业务的类型，常量值为openservice
            map["biz_type"] = "openservice"
            // 签约的支付宝账号对应的支付宝唯一用户号，以2088开头的16位纯数字组成
            map["pid"] = alipaySellerId
            // 产品码，常量值为APP_FAST_LOGIN
            map["product_id"] = "APP_FAST_LOGIN"
            // 授权范围，常量值为kuaijie
            map["scope"] = "kuaijie"
            // 商户标识该次用户授权请求的ID，该值在商户端应保持唯一[userId]
            map["target_id"] = userId.toString()

            // 标识授权类型，取值范围： AUTHACCOUNT代表授权；LOGIN代表登录
            map["auth_type"] = "AUTHACCOUNT"
            // 	商户生成签名字符串所使用的签名算法类型，目前支持RSA2和RSA，推荐使用RSA2
            map["sign_type"] = "RSA2"
            // 整个授权参数信息的签名，即此行以上参数key和value通过&拼接的字符串的签名值，对此字符串签名后需做URL编码
            val signContent = AlipaySignature.getSignContent(map)

            val s = AlipaySignature.rsaSign(signContent, alipayAppPriKey, "utf-8", "RSA2")

            return "${signContent}&sign=${URLEncoder.encode(s, "UTF-8")}"
        }
    }


}